/*
 * vendor/amlogic/media/common/codec_mm/codec_mm_scatter.c
 *
 * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cma.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/genalloc.h>
#include <linux/dma-mapping.h>
#include <linux/dma-map-ops.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/completion.h>
#include <linux/sched/clock.h>

#include "codec_mm_priv.h"
#include "codec_mm_scatter_priv.h"
#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_CODEC_MM
#include <linux/amlogic/meson_atrace.h>

#define SCATTER_MEM "SCATTER_MEM"

#if PAGE_SHIFT >= 12
#define SID_MASK 0xfff
#elif PAGE_SHIFT >= 8
#define SID_MASK 0xff
#else
#error "unsupport PAGE_SHIFT PAGE_SHIFT must >= 8" ## PAGE_SHIFT
#endif

#define MAX_ADDR_SHIFT PAGE_SHIFT
#define MAX_SID (SID_MASK - 1)
#define MAX_HASH_SID (MAX_SID - 1)

#define ADDR_SEED(paddr) (((paddr) >> (MAX_ADDR_SHIFT << 1)) + ((paddr) >> MAX_ADDR_SHIFT) + (paddr))

/*
 * hash ID: 0-MAX_HASH_SID
 * ONE_PAGE_SID: MAX_SID
 */
#define HASH_PAGE_ADDR(paddr) (ADDR_SEED(paddr) % MAX_SID)
#define SLOT_TO_SID(slot) HASH_PAGE_ADDR((((slot)->phy_addr) >> PAGE_SHIFT))

#define ONE_PAGE_SID (SID_MASK)
#define PAGE_SID(mm_page) (page_sid_type)((mm_page)&SID_MASK)
#define PAGE_ADDR(mm_page) (ulong)(((mm_page) >> MAX_ADDR_SHIFT) << MAX_ADDR_SHIFT)

#define ADDR2PAGE(addr, sid) (((addr) & (~SID_MASK)) | (sid))

#define PAGE_SID_OF_MMS(mms, id) PAGE_SID((mms)->pages_list[id])
#define PAGE_ADDR_OF_MMS(mms, id) PAGE_ADDR((mms)->pages_list[id])

#define INVALID_ID(mms, id) (!(mms) || !(mms)->pages_list || (mms)->page_cnt <= (id) || (id) < 0)

#define SID_OF_ONEPAGE(sid) ((sid) == ONE_PAGE_SID)
#define ADDR2BIT(base, addr) (((addr) - (base)) >> PAGE_SHIFT)
#define BIT2ADDR(base, bit) ((base) + (1 << PAGE_SHIFT) * (bit))
#define VALID_SID(sid) (((sid) < MAX_SID) || SID_OF_ONEPAGE(sid))
#define VALID_BIT(slot, bit) ((bit) >= 0 && (((slot)->pagemap_size << 3) > (bit)))
#define CODEC_MM_S_ERR(x) ((-1000) - (x))

#define ERR_LOG(args...) pr_err(args)
#define WAR_LOG(args...) pr_warn(args)
#define INFO_LOG(args...) pr_info(args)

#ifdef SCATTER_DEBUG
#define DBG_LOG(args...) pr_info(args)
#else
#define DBG_LOG(args...)
#endif
#define MAX_SYS_BLOCK_PAGE 128
#define MIN_SYS_BLOCK_PAGE 8

#ifdef USE_KMALLOC_FOR_SCATTER
#define SC_ALLOC(s, f) kmalloc(s, f)
#define SC_FREE(p) kfree(p)
#else
#define SC_ALLOC(s, f) vmalloc(s)
#define SC_FREE(p) vfree(p)

#endif
#define MAX_SC_LIST 64
#define MK_TAG(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
#define SMGT_IDENTIFY_TAG MK_TAG('Z', 'S', 'C', 'Z')

struct codec_mm_scatter_s {
    u32 keep_size_PAGE;
    u32 reserved_block_mm_M;
    u32 try_alloc_in_cma_page_cnt;
    u32 try_alloc_in_sys_page_cnt_max;
    u32 try_alloc_in_sys_page_cnt_min;
    u32 enable_slot_from_sys;
    u32 no_cache_size_M;
    u32 support_from_slot_sys;
    u32 no_alloc_from_sys;
};

struct codec_mm_scatter_mgt {
    unsigned int tag; /*=*/
    struct codec_mm_slot *slot_list_map[MAX_SID];
    int tvp_mode;
    int codec_mm_num;
    int total_page_num;
    int alloced_page_num;
    int max_alloced;
    u32 try_alloc_in_cma_page_cnt;
    u32 try_alloc_in_sys_page_cnt;
    u32 try_alloc_in_sys_page_cnt_max;
    u32 try_alloc_in_sys_page_cnt_min;
    int alloc_from_cma_first;
    u32 enable_slot_from_sys;
    u32 no_cache_size_M;
    u32 no_alloc_from_sys;
    u32 support_from_slot_sys;
    int one_page_cnt;
    int scatters_cnt;
    int slot_cnt;
    u32 reserved_block_mm_M;
    u32 keep_size_PAGE;
    int mem_flags;

    int alloc_from_sys_sc_cnt;
    int alloc_from_sys_page_cnt;
    int alloc_from_sys_max_page_cnt;

    int delay_free_on;
    int force_cache_on;
    int force_cache_page_cnt;
    u64 delay_free_timeout_jiffies64;

    /* time states */
    int alloc_max_us;
    u64 alloc_total_us;
    int alloc_cnt;
    int alloc_10us_less_cnt;
    int alloc_10_50us_cnt;
    int alloc_50_100us_cnt;
    int alloc_100_1000us_cnt;
    int alloc_1_10ms_cnt;
    int alloc_10_100ms_cnt;
    int alloc_100ms_up_cnt;
    /* free time states */
    int free_max_us;
    u64 free_total_us;
    int free_cnt;
    int free_10us_less_cnt;
    int free_10_50us_cnt;
    int free_50_100us_cnt;
    int free_100_1000us_cnt;
    int free_1_10ms_cnt;
    int free_10_100ms_cnt;
    int free_100ms_up_cnt;

    struct delayed_work dealy_work;
    int scatter_task_run_num;
    struct codec_mm_scatter *cache_scs[2];
    int cached_pages;
    spinlock_t list_lock;
    struct mutex monitor_lock;
    struct completion complete;
    struct list_head free_list;                  /* slot */
    struct list_head scatter_list;               /* scatter list */
    struct codec_mm_scatter *scmap[MAX_SC_LIST]; /* used for valid check. */
};
#define is_cache_sc(smgt, mms) (((smgt)->cache_scs[0] == (mms)) || ((smgt)->cache_scs[1] == (mms)))

static struct codec_mm_scatter_s g_scatter;
static struct codec_mm_scatter_mgt *scatter_mgt;
static struct codec_mm_scatter_mgt *scatter_tvp_mgt;
static struct codec_mm_scatter_mgt *codec_mm_get_scatter_mgt(int is_tvp)
{
    if (is_tvp) {
        return scatter_tvp_mgt;
    }
    return scatter_mgt;
}
int codec_mm_scatter_valid_locked(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms);

#ifdef MY_MUTEX_DEBUG
#define codec_mm_scatter_lock(s) codec_mm_scatter_lock_debug(s, __LINE__)

#define codec_mm_list_lock(s) codec_mm_list_lock_debug(s, __LINE__)

static inline int mutex_trylock_time(struct mutex *lock, int wait)
{
    unsigned long timeout = jiffies + wait;
    int locked = mutex_trylock(lock);

    while (!locked && time_before(jiffies, timeout)) {
        msleep(20L);
        locked = mutex_trylock(lock);
    }
    return locked;
}

#define TRY_MLOCK_INFO(lock, line, time, info)                                                                         \
    do {                                                                                                               \
        static int last_lock_line;                                                                                     \
        while (!mutex_trylock_time((lock), time)) {                                                                    \
            pr_err(info " mutex has lock on %d,new lock on %d\n", last_lock_line, line);                               \
        }                                                                                                              \
        last_lock_line = line;                                                                                         \
    } while (0)

static inline int spin_trylock_time(spinlock_t *lock, int wait)
{
    unsigned long timeout = jiffies + wait;
    int locked = spin_trylock(lock);

    while (!locked && time_before(jiffies, timeout)) {
        msleep(20L);
        locked = spin_trylock(lock);
    }
    return locked;
}

#define TRY_SLOCK_INFO(lock, line, time, info)                                                                         \
    do {                                                                                                               \
        static int last_lock_line;                                                                                     \
        while (!spin_trylock_time((lock), time)) {                                                                     \
            pr_err(info " spin has lock on %d,new lock on %d\n", last_lock_line, line);                                \
        }                                                                                                              \
        last_lock_line = line;                                                                                         \
    } while (0)

static inline void codec_mm_scatter_lock_debug(struct codec_mm_scatter *mms, int line)
{
    TRY_MLOCK_INFO(&mms->mutex, line, 10L * HZ, "mms");
}

static inline void codec_mm_list_lock_debug(struct codec_mm_scatter_mgt *smgt, int line)
{
    TRY_SLOCK_INFO(&smgt->list_lock, line, 10L * HZ, "list");
}
#else
static inline void codec_mm_scatter_lock(struct codec_mm_scatter *mms)
{
    mutex_lock(&mms->mutex);
}
static inline int codec_mm_scatter_trylock(struct codec_mm_scatter *mms)
{
    return mutex_trylock(&mms->mutex);
}

static inline void codec_mm_list_lock(struct codec_mm_scatter_mgt *smgt)
{
    spin_lock(&smgt->list_lock);
}
#endif
static inline void codec_mm_scatter_unlock(struct codec_mm_scatter *mms)
{
    mutex_unlock(&mms->mutex);
}

static inline void codec_mm_list_unlock(struct codec_mm_scatter_mgt *smgt)
{
    spin_unlock(&smgt->list_lock);
}

static int codec_mm_scatter_alloc_want_pages_in(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms,
                                                int want_pages);

static struct workqueue_struct *codec_mm_scatter_wq_get(void)
{
    static struct workqueue_struct *codec_mm_scatter_wq;

    if (!codec_mm_scatter_wq) {
        codec_mm_scatter_wq = create_singlethread_workqueue("codec_mm_sc");
    }
    return codec_mm_scatter_wq;
}

static int codec_mm_schedule_delay_work(struct codec_mm_scatter_mgt *smgt, int delay_ms, int for_update)
{
    bool ret;
    if (!for_update && delayed_work_pending(&smgt->dealy_work)) {
        return 0;
    }
    if (delayed_work_pending(&smgt->dealy_work)) {
        cancel_delayed_work(&smgt->dealy_work);
    }
    if (codec_mm_scatter_wq_get()) {
        ret = queue_delayed_work(codec_mm_scatter_wq_get(), &smgt->dealy_work, delay_ms * HZ / 1000L);
    } else {
        ret = schedule_delayed_work(&smgt->dealy_work, delay_ms * HZ / 1000L);
    }
    return ret;
}

static inline u64 codec_mm_get_current_us(void)
{
    return div64_u64(local_clock(), 1000L);
}

static void codec_mm_update_alloc_time(struct codec_mm_scatter_mgt *smgt, u64 startus)
{
    int spend_time_us;

    spend_time_us = (int)(codec_mm_get_current_us() - startus);
    if (spend_time_us > 0 && spend_time_us < 100000000L) {
        /* >0 && less than 100s */
        /* else think time base changed. */
        smgt->alloc_cnt++;
        if (spend_time_us < 10L) {
            smgt->alloc_10us_less_cnt++;
        } else if (spend_time_us < 50L) {
            smgt->alloc_10_50us_cnt++;
        } else if (spend_time_us < 100L) {
            smgt->alloc_50_100us_cnt++;
        } else if (spend_time_us < 1000L) {
            smgt->alloc_100_1000us_cnt++;
        } else if (spend_time_us < 10000L) {
            smgt->alloc_1_10ms_cnt++;
        } else if (spend_time_us < 100000L) {
            smgt->alloc_10_100ms_cnt++;
        } else {
            smgt->alloc_100ms_up_cnt++;
        }

        smgt->alloc_total_us += spend_time_us;
        if (spend_time_us > smgt->alloc_max_us) {
            smgt->alloc_max_us = spend_time_us;
        }
    }
}

static void codec_mm_update_free_time(struct codec_mm_scatter_mgt *smgt, u64 startus)
{
    int spend_time_us;

    spend_time_us = (int)(codec_mm_get_current_us() - startus);
    if (spend_time_us > 0 && spend_time_us < 100000000L) {
        /* >0 && less than 100s */
        /* else think time base changed. */
        smgt->free_cnt++;
        if (spend_time_us < 10L) {
            smgt->free_10us_less_cnt++;
        } else if (spend_time_us < 50L) {
            smgt->free_10_50us_cnt++;
        } else if (spend_time_us < 100L) {
            smgt->free_50_100us_cnt++;
        } else if (spend_time_us < 1000L) {
            smgt->free_100_1000us_cnt++;
        } else if (spend_time_us < 10000L) {
            smgt->free_1_10ms_cnt++;
        } else if (spend_time_us < 100000L) {
            smgt->free_10_100ms_cnt++;
        } else {
            smgt->free_100ms_up_cnt++;
        }

        smgt->free_total_us += spend_time_us;
        if (spend_time_us > smgt->free_max_us) {
            smgt->free_max_us = spend_time_us;
        }
    }
}

static void codec_mm_clear_alloc_infos_in(struct codec_mm_scatter_mgt *smgt)
{
    smgt->alloc_cnt = 0;
    smgt->alloc_10us_less_cnt = 0;
    smgt->alloc_10_50us_cnt = 0;
    smgt->alloc_50_100us_cnt = 0;
    smgt->alloc_100_1000us_cnt = 0;
    smgt->alloc_1_10ms_cnt = 0;
    smgt->alloc_10_100ms_cnt = 0;
    smgt->alloc_100ms_up_cnt = 0;
    smgt->alloc_total_us = 0;
    smgt->alloc_max_us = 0;

    smgt->free_cnt = 0;
    smgt->free_10us_less_cnt = 0;
    smgt->free_10_50us_cnt = 0;
    smgt->free_50_100us_cnt = 0;
    smgt->free_100_1000us_cnt = 0;
    smgt->free_1_10ms_cnt = 0;
    smgt->free_10_100ms_cnt = 0;
    smgt->free_100ms_up_cnt = 0;
    smgt->free_total_us = 0;
    smgt->free_max_us = 0;
}
void codec_mm_clear_alloc_infos(void)
{
    codec_mm_clear_alloc_infos_in(codec_mm_get_scatter_mgt(0));
    codec_mm_clear_alloc_infos_in(codec_mm_get_scatter_mgt(1));
}

static int codec_mm_set_slot_in_hash(struct codec_mm_scatter_mgt *smgt, struct codec_mm_slot *slot)
{
    page_sid_type sid = SLOT_TO_SID(slot);

    if (sid > MAX_SID) {
        ERR_LOG("ERROR sid %d", sid);
        return -1;
    }
    slot->sid = sid;
    INIT_LIST_HEAD(&slot->sid_list);
    INIT_LIST_HEAD(&slot->free_list);
    codec_mm_list_lock(smgt);
    if (!smgt->slot_list_map[sid]) {
        smgt->slot_list_map[sid] = slot;
        slot->isroot = 1;
    } else {
        struct codec_mm_slot *f_slot = smgt->slot_list_map[sid];

        list_add_tail(&slot->sid_list, &f_slot->sid_list);
        slot->isroot = 0;
    }
    smgt->total_page_num += slot->page_num;
    smgt->slot_cnt++;
    if (slot->from_type == SLOT_FROM_GET_FREE_PAGES) {
        smgt->alloc_from_sys_sc_cnt++;
        smgt->alloc_from_sys_page_cnt += slot->page_num;
        if (smgt->alloc_from_sys_page_cnt > smgt->alloc_from_sys_max_page_cnt) {
            smgt->alloc_from_sys_max_page_cnt = smgt->alloc_from_sys_page_cnt;
        }
    }
    list_add_tail(&slot->free_list, &smgt->free_list);
    codec_mm_list_unlock(smgt);
    return 0;
}

static struct codec_mm_slot *codec_mm_find_slot_in_hash(struct codec_mm_scatter_mgt *smgt, page_sid_type sid,
                                                        ulong addr)
{
    struct codec_mm_slot *fslot, *slot;

    if (!VALID_SID(sid) || SID_OF_ONEPAGE(sid)) {
        return NULL;
    }
    codec_mm_list_lock(smgt);
    fslot = smgt->slot_list_map[sid];
    if (!fslot) {
        ERR_LOG("not valid sid %d\n", (int)sid);
        goto err;
    }
    slot = fslot;
    while (!(addr >= slot->phy_addr && /* optimization with hash? */
             addr < (slot->phy_addr + (slot->page_num << PAGE_SHIFT)))) {
        /* not in range. */

        slot = list_entry(slot->sid_list.prev, struct codec_mm_slot, sid_list);
        if (slot == fslot) {
            ERR_LOG("can't find valid slot, for addr =%p\n", (void *)addr);
            goto err;
        }
    }
    codec_mm_list_unlock(smgt);
    return slot;
err:
    codec_mm_list_unlock(smgt);
    return NULL;
}

static int codec_mm_slot_free(struct codec_mm_scatter_mgt *smgt, struct codec_mm_slot *slot)
{
    int ret = 0;

    codec_mm_list_lock(smgt);
    if (slot->alloced_page_num > 0 || slot->on_alloc_free) {
        codec_mm_list_unlock(smgt);
        return -1;
    }
    if (!list_empty(&slot->free_list)) {
        list_del(&slot->free_list);
    }
    if (!list_empty(&slot->sid_list)) {
        if (slot->isroot) {
            struct codec_mm_slot *next_slot;

            next_slot = list_entry(slot->sid_list.next, struct codec_mm_slot, sid_list);
            next_slot->isroot = 1;
            smgt->slot_list_map[slot->sid] = next_slot;
        }
        list_del(&slot->sid_list);
    } else { /* no sid list,clear map */
        smgt->slot_list_map[slot->sid] = NULL;
    }
    smgt->slot_cnt--;
    smgt->total_page_num -= slot->page_num;
    if (slot->from_type == SLOT_FROM_GET_FREE_PAGES) {
        smgt->alloc_from_sys_sc_cnt--;
        smgt->alloc_from_sys_page_cnt -= slot->page_num;
    }
    codec_mm_list_unlock(smgt);
    switch (slot->from_type) {
        case SLOT_FROM_CODEC_MM:
            if (slot->mm) {
                codec_mm_release(slot->mm, SCATTER_MEM);
            } else {
                ERR_LOG("ERR:slot->mm is ERROR:%p\n", slot->mm);
            }
            break;
        case SLOT_FROM_GET_FREE_PAGES:
            if (slot->page_header != 0) {
                free_pages(slot->page_header, get_order(PAGE_SIZE * slot->page_num));
            } else {
                ERR_LOG("ERR:slot->page_header is ERROR:%p\n", (void *)slot->page_header);
            }
            break;
            /* other */
        default:
            ERR_LOG("unknown from type:%d\n", slot->from_type);
            ret = -1;
    }

    kfree(slot->pagemap);
    kfree(slot);
    return 0;
}
static int codec_mm_slot_try_free(struct codec_mm_scatter_mgt *smgt, struct codec_mm_slot *slot)
{
    if (smgt->keep_size_PAGE > 0) {
        /* delay free, when size < delay_free_M MB */
        int free_pages = smgt->total_page_num - smgt->alloced_page_num;

        if (free_pages < smgt->keep_size_PAGE) {
            return -1;
        }
    }
    return codec_mm_slot_free(smgt, slot);
}

static inline int codec_mm_slot_init_bitmap(struct codec_mm_slot *slot)
{
    slot->alloced_page_num = 0;
    /* bytes = 8bits for 8 pages.
     * 1 more for less than 8 page.
     * another for reserved.
     */
    slot->pagemap_size = (slot->page_num >> 3L) + 2L;
    slot->pagemap = kmalloc(slot->pagemap_size, GFP_KERNEL);
    if (!slot->pagemap) {
        ERR_LOG("ERROR.init pagemap failed\n");
        return -1;
    }
    memset(slot->pagemap, 0, slot->pagemap_size);
    slot->next_bit = 0;
    return 0;
}

/*
 * flags : 1. don't used codecmm.
 */
static struct codec_mm_slot *codec_mm_slot_alloc(struct codec_mm_scatter_mgt *smgt, int size, int flags)
{
    struct codec_mm_slot *slot;
    struct codec_mm_s *mm;
    int try_alloc_size = size;
    int have_alloced = 0;

    if (try_alloc_size > 0 && try_alloc_size <= PAGE_SIZE) {
        return NULL; /* don't alloc less than one PAGE. */
    }
    slot = kmalloc(sizeof(struct codec_mm_slot), GFP_KERNEL);
    if (!slot) {
        return NULL;
    }
    memset(slot, 0, sizeof(struct codec_mm_slot));
    do {
        if (flags & 1) {
            break; /* ignore codec_mm */
        }
        if ((try_alloc_size <= 0 || try_alloc_size > 64L * 1024L) && /* must > 512K. */
            (smgt->tvp_mode || (codec_mm_get_free_size() > smgt->reserved_block_mm_M * SZ_1M))) {
            /* try from codec_mm */
            if (try_alloc_size <= 0) {
                try_alloc_size = smgt->try_alloc_in_cma_page_cnt * PAGE_SIZE;
            }
            if (codec_mm_get_free_size() < try_alloc_size) {
                try_alloc_size = codec_mm_get_free_size();
            }
            mm = codec_mm_alloc(SCATTER_MEM, try_alloc_size, 0,
                                CODEC_MM_FLAGS_FOR_VDECODER | CODEC_MM_FLAGS_FOR_SCATTER |
                                    (smgt->tvp_mode ? CODEC_MM_FLAGS_TVP : 0));
            if (mm != NULL) {
                slot->from_type = SLOT_FROM_CODEC_MM;
                slot->mm = mm;
                slot->page_num = mm->page_count;
                slot->phy_addr = mm->phy_addr;
                codec_mm_slot_init_bitmap(slot);
                if (slot->pagemap == NULL) {
                    codec_mm_release(mm, SCATTER_MEM);
                    break; /* try next. */
                }
                have_alloced = 1;
                DBG_LOG("alloced from codec mm %d!!!\n", slot->page_num);
            }
        }
    } while (0);
    if (!have_alloced && !smgt->support_from_slot_sys) {
        /* not enabled from sys */
        goto error;
    }
    if (!have_alloced) { /* init for sys alloc */
        if (size <= 0) {
            try_alloc_size = smgt->try_alloc_in_sys_page_cnt << PAGE_SHIFT;
        } else {
            try_alloc_size = PAGE_ALIGN(size);
        }
        if (try_alloc_size <= PAGE_SIZE << 1) {
            DBG_LOG("try too small %d, try one page now,\n", try_alloc_size);
            goto error; /* don't alloc 1 page with slot. */
        }
    }
    if (!have_alloced) {
        /* tvp not support alloc from sys. */
        if (smgt->tvp_mode || smgt->no_alloc_from_sys) {
            goto error;
        }
    }
    while (!have_alloced) {
        /* don't alloc 1 page with slot. */
        /* try alloc from sys. */
        int page_order = get_order(try_alloc_size);
        slot->page_header = __get_free_pages(__GFP_IO | __GFP_NOWARN | __GFP_NORETRY, page_order);
        if (!slot->page_header) {
            if ((try_alloc_size >> (PAGE_SHIFT + 1)) >= smgt->try_alloc_in_sys_page_cnt_min) {
                try_alloc_size = try_alloc_size >> 1;
                smgt->try_alloc_in_sys_page_cnt = try_alloc_size >> PAGE_SHIFT;
                continue; /* try less block memory. */
            } else {
                /* disabled from sys
                 * may auto enabled when free more.
                 */
                smgt->support_from_slot_sys = 0;
                goto error;
            }
        }
        slot->from_type = SLOT_FROM_GET_FREE_PAGES;
        slot->mm = NULL;
        slot->page_num = 1 << page_order;
        slot->phy_addr = virt_to_phys((unsigned long *)slot->page_header);
        codec_mm_slot_init_bitmap(slot);
        if (slot->pagemap == NULL) {
            free_pages(slot->page_header, page_order);
            goto error;
        }
        have_alloced = 1;
        break;
    }
    codec_mm_set_slot_in_hash(smgt, slot);

    return slot;
error:
    kfree(slot);
    return NULL;
}

static int codec_mm_slot_free_page(struct codec_mm_scatter_mgt *smgt, struct codec_mm_slot *slot, ulong phy_addr)
{
    int bit;

    if (!slot || !slot->pagemap || slot->page_num <= 0) {
        return CODEC_MM_S_ERR(4L);
    }

    bit = ADDR2BIT(slot->phy_addr, phy_addr);
    if (!VALID_BIT(slot, bit)) {
        return CODEC_MM_S_ERR(5L); /*!!!out of page map.!! */
    }
    codec_mm_list_lock(smgt);
    slot->on_alloc_free++;
    if (!test_and_clear_bit(bit, slot->pagemap)) {
        ERR_LOG("ERROR,page is ready free before!!! page=%p\n", (void *)phy_addr);
        slot->on_alloc_free--;
        codec_mm_list_unlock(smgt);
        return CODEC_MM_S_ERR(6L);
    }
    slot->alloced_page_num--;
    slot->on_alloc_free--;
    codec_mm_list_unlock(smgt);
    return 0;
}

static int codec_mm_slot_alloc_pages(struct codec_mm_scatter_mgt *smgt, struct codec_mm_slot *slot,
                                     phy_addr_type *pages, int num)
{
    int alloced = 0;
    int need = num;
    int can_alloced;
    phy_addr_type page;
    int tryn;
    int i;

    if (!slot || !slot->pagemap) {
        return -1;
    }
    if (slot->alloced_page_num >= slot->page_num) {
        return -2L;
    }
    tryn = slot->page_num;
    can_alloced = slot->page_num - slot->alloced_page_num;
    need = need > can_alloced ? can_alloced : need;
    i = slot->next_bit;
    /* if not one alloc free. quit this one */
    while (need > 0 && (slot->on_alloc_free == 1)) {
        if (!VALID_BIT(slot, i)) {
            ERR_LOG("ERROR alloc in slot %p\n", slot);
            ERR_LOG("\ti=%d,slot->pagemap=%p\n", i, slot->pagemap);
            break;
        }
        codec_mm_list_lock(smgt);
        if (!test_and_set_bit(i, slot->pagemap)) {
            page = ADDR2PAGE(BIT2ADDR(slot->phy_addr, i), slot->sid);
            slot->alloced_page_num++;
            pages[alloced] = page;
            alloced++;
            need--;
        }
        codec_mm_list_unlock(smgt);
        i++;
        if (i >= slot->page_num) {
            i = 0;
        }
        if (--tryn <= 0) {
            break;
        }
    }
    slot->next_bit = i;
    if (i >= slot->page_num) {
        slot->next_bit = 0;
    }
    DBG_LOG("alloced from %p, %d,%d,%d\n", slot, slot->page_num, slot->alloced_page_num, alloced);
    return alloced;
}

static inline phy_addr_type codec_mm_get_page_addr(struct codec_mm_scatter *mms, int id)
{
    phy_addr_type page;

    if (INVALID_ID(mms, id)) {
        return 0;
    }
    page = mms->pages_list[id];
    return PAGE_ADDR(page);
}

static inline page_sid_type codec_mm_get_page_sid(struct codec_mm_scatter *mms, int id)
{
    phy_addr_type page;

    if (INVALID_ID(mms, id)) {
        return 0;
    }
    page = mms->pages_list[id];
    return PAGE_SID(page);
}

static int codec_mm_page_free_to_slot(struct codec_mm_scatter_mgt *smgt, page_sid_type sid, ulong addr)
{
    struct codec_mm_slot *slot;
    int ret;
    int slot_free = 0;

    slot = codec_mm_find_slot_in_hash(smgt, sid, addr);
    if (!slot) {
        return -1;
    }
    ret = codec_mm_slot_free_page(smgt, slot, addr);
    if (ret != 0) {
        ERR_LOG("free slot addr error =%p ret=%d\n", (void *)addr, ret);
    }

    if (slot->alloced_page_num == 0) {
        slot_free = (codec_mm_slot_try_free(smgt, slot) == 0);
    }

    if (!slot_free) { /* move to have free list. */
        codec_mm_list_lock(smgt);
        if (list_empty(&slot->free_list) && (slot->alloced_page_num < slot->page_num)) {
            DBG_LOG("add to  free %p, %d,%d,%d\n", slot, slot->page_num, slot->alloced_page_num, ret);
            list_add_tail(&slot->free_list, &smgt->free_list);
        }
        codec_mm_list_unlock(smgt);
    }

    return 0;
}

static int codec_mm_page_alloc_from_one_pages(struct codec_mm_scatter_mgt *smgt, phy_addr_type *pages, int num)
{
    int neednum = num;
    int alloced = 0;

    while (neednum > 0) { /* one page  alloc */
        void *vpage = (void *)__get_free_page(GFP_KERNEL);
        ulong page;
        page_sid_type sid;

        if (vpage != NULL) {
            page = virt_to_phys(vpage);
            sid = ONE_PAGE_SID;
            page |= sid;
            pages[alloced++] = page;
            neednum--;
        } else {
            /* can't alloced memofy from ONEPAGE alloc */
            WAR_LOG("Out of memory OnePage alloc =%d,%d\n", alloced, num);
            break;
        }
    }
    codec_mm_list_lock(smgt);
    smgt->total_page_num += alloced;
    smgt->one_page_cnt += alloced;
    smgt->alloced_page_num += alloced;
    codec_mm_list_unlock(smgt);
    return alloced;
}

static int codec_mm_page_alloc_from_slot(struct codec_mm_scatter_mgt *smgt, phy_addr_type *pages, int num)
{
    struct codec_mm_slot *slot = NULL;
    int alloced = 0;
    int neednum = num;
    int n;

    codec_mm_list_lock(smgt);
    if (!smgt->tvp_mode && list_empty(&smgt->free_list) &&
        (codec_mm_get_free_size() < /* no codec mm */
         smgt->reserved_block_mm_M * SZ_1M) &&
        !smgt->support_from_slot_sys) { /* no sys */
        codec_mm_list_unlock(smgt);
        return 0;
    }
    codec_mm_list_unlock(smgt);

    do {
        slot = NULL;
        if (smgt->total_page_num <= 0 || /* no codec mm. */
            smgt->alloced_page_num == smgt->total_page_num || list_empty(&smgt->free_list)) {
            slot = codec_mm_slot_alloc(smgt, 0, 0);
            if (!slot) {
                u32 alloc_pages = smgt->try_alloc_in_cma_page_cnt / 4L;
                if (codec_mm_get_sc_debug_mode() & 0x01) {
                    pr_info("alloc default cma size fail, try %d pages\n", alloc_pages);
                }
                slot = codec_mm_slot_alloc(smgt, alloc_pages * PAGE_SIZE, 0);
                if (!slot) {
                    if (codec_mm_get_sc_debug_mode() & 0x01) {
                        pr_info("slot alloc 4M fail!\n");
                    }
                    break;
                }
            }
        }
        codec_mm_list_lock(smgt);
        if (slot && slot->on_alloc_free != 0) {
            ERR_LOG("the slot on alloc/free1: %d\n", slot->on_alloc_free);
            slot = NULL; /* slot used on another alloc. */
        }
        if (!slot && !list_empty(&smgt->free_list)) {
            slot = list_entry(smgt->free_list.next, struct codec_mm_slot, free_list);
            if (!slot) {
                ERR_LOG("ERROR!!!!.slot is NULL!!!!\n");
            } else if (slot->on_alloc_free != 0) {
                ERR_LOG("the slot on alloc/free: %d\n", slot->on_alloc_free);
                slot = NULL;
            }
        }
        if (slot && slot->on_alloc_free == 0) { /* del from free list. */
            slot->on_alloc_free++;
            list_del_init(&slot->free_list);
        } else {
            slot = NULL;
        }
        codec_mm_list_unlock(smgt);
        if (slot) {
            n = codec_mm_slot_alloc_pages(smgt, slot, &pages[alloced], neednum);
            codec_mm_list_lock(smgt);
            slot->on_alloc_free--; /* alloc use end */
            if (slot->alloced_page_num < slot->page_num && list_empty(&slot->free_list)) {
                DBG_LOG("slot have free: %p, t:%d,a:%d,%d\n", slot, slot->page_num, slot->alloced_page_num, alloced);
                list_add_tail(&slot->free_list, &smgt->free_list);
                /* no free now, del from free list., */
                /* and init to empty */
            }
            codec_mm_list_unlock(smgt);
            if (n > 0) {
                alloced += n;
                neednum -= n;
            } else {
                DBG_LOG("alloced fail neednum:%d n=%d\n", neednum, n);
                DBG_LOG("smgt->free_list.next:%p\n", smgt->free_list.next);
                DBG_LOG("smgt->free_list.prev:%p\n", smgt->free_list.prev);
                DBG_LOG("slot->free_list:%p\n", &slot->free_list);
                DBG_LOG("slot->free_list.next:%p\n", slot->free_list.next);
                continue; /* try next. */
            }
        } else {
            /* not alloced enough in block mode, try one page next */
            break;
        }
    } while (neednum > 0);
    if (neednum > 0 && 0) {
        WAR_LOG("1can't alloc enough pages!!alloced=%d,need=%d\n", alloced, num);
        WAR_LOG("2can't alloc enough pages!!alloced=%d,need=%d\n", alloced, num);
    }
    codec_mm_list_lock(smgt);
    smgt->alloced_page_num += alloced;
    if (smgt->max_alloced < smgt->alloced_page_num) {
        smgt->max_alloced = smgt->alloced_page_num;
    }
    codec_mm_list_unlock(smgt);
    return alloced;
}

/* flags & 1; alloc. */
static struct codec_mm_scatter *codec_mm_get_next_cache_scatter(struct codec_mm_scatter_mgt *smgt,
                                                                struct codec_mm_scatter *cur_mms, int flags)
{
    struct codec_mm_scatter *mms = NULL;

    codec_mm_list_lock(smgt);
    if (cur_mms) {
        if (smgt->cache_scs[0] == cur_mms) {
            mms = smgt->cache_scs[1];
        } else {
            mms = smgt->cache_scs[0];
        }
    } else if (smgt->cache_scs[0] && smgt->cache_scs[1]) {
        int more_pages = flags & 1;
        int id = -1;

        if (smgt->cache_scs[0]->page_cnt >= smgt->cache_scs[1]->page_cnt) {
            id = more_pages ? 0 : 1;
        } else {
            id = more_pages ? 1 : 0;
        }
        mms = smgt->cache_scs[id];
    } else {
        if (smgt->cache_scs[0]) {
            mms = smgt->cache_scs[0];
        } else {
            mms = smgt->cache_scs[1];
        }
    }
    codec_mm_list_unlock(smgt);
    return mms;
}

static int codec_mm_page_alloc_from_free_scatter(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *src_mms,
                                                 phy_addr_type *pages, int num, int wait)
{
    struct codec_mm_scatter *mms;
    int need = num;
    int alloced = 0;

    mms = src_mms;
    if (!mms) {
        return 0;
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_1);
    if (!wait) {
        if (!codec_mm_scatter_trylock(mms)) {
            return 0; /* mms is locked try another. */
        }
    } else {
        codec_mm_scatter_lock(mms);
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_2);
    alloced = min(mms->page_cnt, need);
    if (alloced > 0) {
        mms->page_cnt -= alloced;
        mms->page_tail -= alloced;

        memcpy(pages, &mms->pages_list[mms->page_tail + 1], alloced * sizeof(phy_addr_type));
        memset(&mms->pages_list[mms->page_tail + 1], 0, alloced * sizeof(phy_addr_type));
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_3);
    codec_mm_list_lock(smgt);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_4);
    smgt->cached_pages -= alloced;
    codec_mm_list_unlock(smgt);
    codec_mm_scatter_unlock(mms);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_cache_scatter_5);
    return alloced;
}

static int codec_mm_page_alloc_from_cache_scatter(struct codec_mm_scatter_mgt *smgt, phy_addr_type *pages, int num)
{
    struct codec_mm_scatter *src_mms;
    int alloced;

    src_mms = codec_mm_get_next_cache_scatter(smgt, NULL, 1);
    alloced = codec_mm_page_alloc_from_free_scatter(smgt, src_mms, pages, num, 0);
    if (alloced < num) {
        src_mms = codec_mm_get_next_cache_scatter(smgt, src_mms, 1);
        alloced += codec_mm_page_alloc_from_free_scatter(smgt, src_mms, &pages[alloced], num - alloced, 1);
    }
    return alloced;
}

static int codec_mm_page_alloc_all_locked(struct codec_mm_scatter_mgt *smgt, phy_addr_type *pages, int num, int iscache)
{
    int alloced = 0;
    int can_from_scatter = iscache ? 0 : 1;
    int can_from_slot = 1;
    int new_alloc;

    while (alloced < num) {
        new_alloc = 0;
        if (can_from_scatter) {
            ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_free_scatter);
            new_alloc = codec_mm_page_alloc_from_cache_scatter(smgt, pages + alloced, num - alloced);
            ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_free_scatter_end);
            if (new_alloc <= 0) {
                can_from_scatter = 0;
            }
        } else if (can_from_slot) {
            ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_slot);
            new_alloc = codec_mm_page_alloc_from_slot(smgt, pages + alloced, num - alloced);
            ATRACE_COUNTER("mmu alloc", MMU_ALLOC_from_slot_end);
            if (new_alloc <= 0) {
                can_from_slot = 0;
            }
        } else if (!smgt->no_alloc_from_sys && !smgt->tvp_mode) {
            new_alloc = codec_mm_page_alloc_from_one_pages(smgt, pages + alloced, num - alloced);
            if (new_alloc <= 0) {
                break;
            }
        } else {
            break;
        }
        alloced += new_alloc;
    }
    return alloced;
}

static int codec_mm_pages_free_to_scatter(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *src_mms,
                                          struct codec_mm_scatter *dst_mms, int wait)
{
    int moved = 0;
    int left;

    if (src_mms->page_used >= src_mms->page_cnt) {
        return -1; /* no need free. */
    }
    if (!dst_mms || src_mms == dst_mms) {
        return 0;
    }
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS);
    if (!wait) {
        if (!codec_mm_scatter_trylock(dst_mms)) {
            return -2L; /* mms is locked try another. */
        }
    } else {
        codec_mm_scatter_lock(dst_mms);
    }
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS_LOCK);
    moved = min(src_mms->page_cnt - src_mms->page_used, dst_mms->page_max_cnt - dst_mms->page_cnt);
    left = src_mms->page_cnt - moved;
    if (moved > 0) {
        memcpy(&dst_mms->pages_list[dst_mms->page_tail + 1], &src_mms->pages_list[left], moved * sizeof(phy_addr_type));
        memset(&src_mms->pages_list[left], 0, sizeof(phy_addr_type) * moved);
    }
    dst_mms->page_cnt += moved;
    dst_mms->page_tail += moved;
    src_mms->page_cnt -= moved;
    src_mms->page_tail -= moved;
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT);
    codec_mm_list_lock(smgt);
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT_LOCK);
    smgt->cached_pages += moved;
    codec_mm_list_unlock(smgt);
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_SMGT_LOCK_DONE);
    codec_mm_scatter_unlock(dst_mms);
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_DTS_LOCK_DONE);
    return moved;
}

static int codec_mm_page_free_to_cache_scatter(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *src_mms)
{
    struct codec_mm_scatter *dst_mms;
    int alloced;

    dst_mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
    alloced = codec_mm_pages_free_to_scatter(smgt, src_mms, dst_mms, 0);
    if (alloced == -2L) {
        dst_mms = codec_mm_get_next_cache_scatter(smgt, dst_mms, 0);
        alloced += codec_mm_pages_free_to_scatter(smgt, src_mms, dst_mms, 1);
    }
    return alloced;
}

/*
 * free one page in mms;
 */
static int codec_mm_scatter_free_page_id_locked(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms, int id)
{
    page_sid_type sid;
    int ret;

    if (INVALID_ID(mms, id)) {
        return CODEC_MM_S_ERR(1);
    }
    sid = PAGE_SID_OF_MMS(mms, id);
    if (!VALID_SID(sid)) {
        return CODEC_MM_S_ERR(2L);
    }
    if (SID_OF_ONEPAGE(sid)) {
        ulong phy_addr = PAGE_ADDR_OF_MMS(mms, id);

        free_page((unsigned long)phys_to_virt(phy_addr));
        codec_mm_list_lock(smgt);
        smgt->one_page_cnt--;
        smgt->total_page_num--;
        codec_mm_list_unlock(smgt);
        if (id == mms->page_tail) {
            mms->page_tail--;
        }
        mms->page_cnt--;
        return 0;
    }
    ret = codec_mm_page_free_to_slot(smgt, sid, PAGE_ADDR_OF_MMS(mms, id));
    if (!ret) {
        mms->page_cnt--;
        mms->page_tail--;
    }
    return ret;
}

/*
 * free one page in mms;
 */
static int codec_mm_scatter_free_pages_in_locked(struct codec_mm_scatter *mms, int start_id)
{
    struct codec_mm_scatter_mgt *smgt = (struct codec_mm_scatter_mgt *)mms->manager;
    int i;
    int ret;
    int id = start_id;
    int freeNum = 0;
    int not_continue_print = 1;

    for (i = mms->page_tail; i >= id; i--) {
        ret = codec_mm_scatter_free_page_id_locked(smgt, mms, i);
        if (ret < 0) {
            if (not_continue_print) {
                ERR_LOG("page free error.%d,id=%d, addr:%d\n", ret, i, (int)mms->pages_list[i]);
                codec_mm_dump_scatter(mms, NULL, 0);
            }
            not_continue_print = 0;
        } else {
            not_continue_print = 1;
        }
        freeNum++;
        mms->pages_list[i] = (phy_addr_type)0;
    }
    codec_mm_list_lock(smgt);
    smgt->alloced_page_num -= freeNum;
    if (is_cache_sc(smgt, mms)) {
        smgt->cached_pages -= freeNum;
    }
    codec_mm_list_unlock(smgt);
    return 0;
}

/*
 * fast_mode:.
 * 1: set page_used: call by owner
 * 2: free to scatter: call by owner
 * 3: free to slot or others: call by owner
 *
 * free_mode:.
 * 0: free from id pages.
 * 1: free not used pages;call by moniter.
 * 2: less pages.: call by moniter for cache
 *   id is negative.
 */
static int codec_mm_scatter_free_tail_pages_in(struct codec_mm_scatter *mms, int id, int fast_mode, int free_mode)
{
    struct codec_mm_scatter_mgt *smgt;
    int start_free_id = id;

    if (!mms) {
        return -1;
    }
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START);
    codec_mm_scatter_lock(mms);
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK);
    if (free_mode == 1) {
        start_free_id = mms->page_used;
    }
    if (free_mode == 2L) {
        if (id > 0) {
            if (id >= mms->page_cnt) {
                start_free_id = 0;
            } else {
                start_free_id = mms->page_cnt - id;
            }
        } else {
            start_free_id = -1;
        }
    }
    if ((start_free_id < 0) || (start_free_id >= mms->page_cnt) || (mms->page_tail < 0)) {
        if (mms && start_free_id != mms->page_cnt) {
            ERR_LOG("mms[%p],free error id %d(%d),cnt=%d ,=m:%d,%d\n", mms, id, start_free_id, mms->page_cnt, fast_mode,
                    free_mode);
        }
        codec_mm_scatter_unlock(mms);
        ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
        return -1;
    }
    smgt = (struct codec_mm_scatter_mgt *)mms->manager;
    codec_mm_list_lock(smgt);
    mms->page_used = start_free_id;
    codec_mm_list_unlock(smgt);

    if (fast_mode == 1) {
        codec_mm_scatter_unlock(mms);
        ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
        return 0;
    }
    if (fast_mode == 2L || fast_mode == 3L) {
        u64 startus;

        startus = codec_mm_get_current_us();
        codec_mm_page_free_to_cache_scatter(smgt, mms);
        codec_mm_update_free_time(smgt, startus);
        if (fast_mode == 2L || mms->page_used == mms->page_cnt) {
            codec_mm_scatter_unlock(mms);
            ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
            return 0;
        }
    }
    codec_mm_scatter_free_pages_in_locked(mms, start_free_id);
    codec_mm_scatter_unlock(mms);
    ATRACE_COUNTER("mmu_free", MMU_FREE_SCATTER_START_LOCK_DONE);
    return 0;
}

int codec_mm_scatter_free_tail_pages(struct codec_mm_scatter *mms, int start_id)
{
    int ret = 0;
    ret = codec_mm_scatter_free_tail_pages_in(mms, start_id, 0, 0);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages);

int codec_mm_scatter_free_tail_pages_fast(struct codec_mm_scatter *mms, int start_free_id)
{
    int ret = 0;
    if (!mms) {
        return -1;
    }
    if (start_free_id < mms->page_cnt) {
        ret = codec_mm_scatter_free_tail_pages_in(mms, start_free_id, 2L, 0);
    }
    codec_mm_schedule_delay_work((struct codec_mm_scatter_mgt *)mms->manager, 100L, 0);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_tail_pages_fast);

int codec_mm_scatter_free_unused_pages(struct codec_mm_scatter *mms)
{
    int ret = 0;
    ret = codec_mm_scatter_free_tail_pages_in(mms, 0, 0, 1);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_unused_pages);

int codec_mm_scatter_less_pages(struct codec_mm_scatter *mms, int nums)
{
    int ret = 0;

    ret = codec_mm_scatter_free_tail_pages_in(mms, nums, 0, 2L);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_less_pages);

/* free all pages only
 * don't free scatter
 */
int codec_mm_scatter_free_all_pages(struct codec_mm_scatter *mms)
{
    int ret;

    ret = codec_mm_scatter_free_tail_pages(mms, 0);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_free_all_pages);

static inline int codec_mm_scatter_map_add_locked(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms)
{
    int i;

    for (i = 0; i < MAX_SC_LIST; i++) {
        if (smgt->scmap[i] == NULL) {
            smgt->scmap[i] = mms;
            return i;
        }
    }
    return -1;
}

static inline int codec_mm_scatter_map_del_locked(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms)
{
    int i;

    for (i = 0; i < MAX_SC_LIST; i++) {
        if (smgt->scmap[i] == mms) {
            smgt->scmap[i] = NULL;
            return i;
        }
    }
    return 0;
}

int codec_mm_scatter_valid_locked(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms)
{
    int i;
    int valid = 0;

    for (i = 0; i < MAX_SC_LIST; i++) {
        if (smgt->scmap[i] == mms) {
            valid = 1;
            break;
        }
    }
    return valid;
}
EXPORT_SYMBOL(codec_mm_scatter_valid_locked);

/* free scatter's all */
static int codec_mm_scatter_free_on_nouser_ext(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms,
                                               int not_in_mgt)
{
    int ret = 0;
    int free;

    codec_mm_scatter_lock(mms);
    codec_mm_list_lock(smgt);
    ret = atomic_read(&mms->user_cnt);
    if (ret > 0) {
        codec_mm_list_unlock(smgt);
        codec_mm_scatter_unlock(mms);
        /* >0 have another user. */
        /* pr_err("ERROR, scatter is not free.cnt:%d\n", ret); */
        return 0;
    }
    /* to free now */
    free = mms->page_cnt;
    if (!list_empty(&mms->list)) {
        list_del(&mms->list);
    }
    if (!not_in_mgt) {
        smgt->scatters_cnt--;
        codec_mm_scatter_map_del_locked(smgt, mms);
    }

    codec_mm_list_unlock(smgt);
    codec_mm_scatter_unlock(mms);
    if (mms->page_cnt > 0) {
        ret = codec_mm_scatter_free_tail_pages_in(mms, 0, 0, 0);
    }
    if (free >= 256L && (smgt->try_alloc_in_sys_page_cnt < smgt->try_alloc_in_sys_page_cnt_max) &&
        (smgt->total_page_num < (1024L * 1024L * 32L >> PAGE_SHIFT))) {
        smgt->try_alloc_in_sys_page_cnt *= 2L;
        if (!smgt->support_from_slot_sys) {
            smgt->support_from_slot_sys = smgt->enable_slot_from_sys;
        }
    }

    SC_FREE(mms);
    return ret;
}
/* free scatter's all */
static int codec_mm_scatter_free_on_nouser(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms)
{
    return codec_mm_scatter_free_on_nouser_ext(smgt, mms, 0);
}

/*
 * mask for other use it.
 */
static int codec_mm_scatter_inc_user_in1(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms, int cnt)
{
    int ret = -1;
    int old_user;

    codec_mm_list_lock(smgt);
    if (!codec_mm_scatter_valid_locked(smgt, mms)) {
        codec_mm_list_unlock(smgt);
        return -1;
    }
    old_user = atomic_read(&mms->user_cnt);
    if (old_user >= 0) {
        ret = atomic_add_return(cnt, &mms->user_cnt);
        if (old_user == 1) {
            mms->tofree_jiffies = jiffies;
        }
    }
    codec_mm_list_unlock(smgt);
    return ret <= 0 ? ret : 0; /* must add before user cnt >= 0 */
}
static int codec_mm_scatter_inc_user_in(struct codec_mm_scatter *mms, int cnt)
{
    struct codec_mm_scatter_mgt *smgt;
    int ret;

    if (!mms) {
        return -1;
    }
    smgt = codec_mm_get_scatter_mgt(0);
    ret = codec_mm_scatter_inc_user_in1(smgt, mms, cnt);
    if (ret < 0) {
        smgt = codec_mm_get_scatter_mgt(1);
        ret = codec_mm_scatter_inc_user_in1(smgt, mms, cnt);
    }
    return ret;
}

/* mask scatter's to free. */
static int codec_mm_scatter_dec_user_in1(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms,
                                         int delay_free_ms, int cnt)
{
    int after_users = 1;

    codec_mm_list_lock(smgt);
    if (!codec_mm_scatter_valid_locked(smgt, mms)) {
        codec_mm_list_unlock(smgt);
        return -1;
    }
    if (atomic_read(&mms->user_cnt) >= 1) {
        after_users = atomic_sub_return(cnt, &mms->user_cnt);
        if (after_users == 0) {
            /* is free time */
            mms->tofree_jiffies = jiffies + delay_free_ms * HZ / 1000L;
        }
    }
    codec_mm_list_unlock(smgt);
    if (after_users == 0) {
        codec_mm_schedule_delay_work(smgt, 0, 1);
    }
    return 0;
}
/* mask scatter's to free. */
static int codec_mm_scatter_dec_user_in(struct codec_mm_scatter *mms, int delay_free_ms, int cnt)
{
    struct codec_mm_scatter_mgt *smgt;
    int ret;

    if (!mms) {
        return -1;
    }
    smgt = codec_mm_get_scatter_mgt(0);
    ret = codec_mm_scatter_dec_user_in1(smgt, mms, delay_free_ms, cnt);
    if (ret < 0) {
        smgt = codec_mm_get_scatter_mgt(1);
        ret = codec_mm_scatter_dec_user_in1(smgt, mms, delay_free_ms, cnt);
    }
    return ret;
}

/*
 * maybe a render/sink.video/osd/
 */
int codec_mm_scatter_inc_for_keeper(void *sc_mm)
{
    struct codec_mm_scatter *mms = sc_mm;

    return codec_mm_scatter_inc_user_in(mms, 100L);
}
EXPORT_SYMBOL(codec_mm_scatter_inc_for_keeper);

/*
 * maybe a render/sink.video/osd/
 */
int codec_mm_scatter_dec_keeper_user(void *sc_mm, int delay_ms)
{
    struct codec_mm_scatter *mms = sc_mm;

    return codec_mm_scatter_dec_user_in(mms, delay_ms, 100L);
}
EXPORT_SYMBOL(codec_mm_scatter_dec_keeper_user);

int codec_mm_scatter_dec_owner_user(void *sc_mm, int delay_ms)
{
    struct codec_mm_scatter *mms = sc_mm;
    int ret = codec_mm_scatter_dec_user_in(mms, delay_ms, 1000L);
    if (ret) {
        ERR_LOG("dec_owner_user error %p\n", mms);
    }
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_dec_owner_user);

struct codec_mm_scatter *codec_mm_scatter_alloc_new(struct codec_mm_scatter_mgt *smgt, int max_page, int page_num)
{
    struct codec_mm_scatter *mms;
    int ret;

    if (max_page < page_num) {
        return NULL;
    }

    mms = SC_ALLOC(sizeof(struct codec_mm_scatter) + sizeof(phy_addr_type) * max_page, GFP_KERNEL);
    if (!mms) {
        ERR_LOG("no enough for mm scatter!!!!\n");
        return NULL;
    }
    memset(mms, 0, sizeof(struct codec_mm_scatter));
    mms->pages_list = (phy_addr_type *)(mms + 1);
    mms->page_max_cnt = max_page;
    INIT_LIST_HEAD(&mms->list);
    memset(mms->pages_list, 0, sizeof(phy_addr_type) * max_page);
    mms->page_cnt = 0;
    mms->page_tail = -1;
    mms->manager = (void *)smgt;
    atomic_set(&mms->user_cnt, 0);
    mutex_init(&mms->mutex);
    if (page_num > 0) {
        ret = codec_mm_page_alloc_all_locked(smgt, mms->pages_list, page_num, is_cache_sc(smgt, mms));
        if (ret <= 0) {
            goto error;
        }
        mms->page_cnt = ret;
        mms->page_tail = mms->page_cnt - 1;
    }
    atomic_set(&mms->user_cnt, 1000L);
    codec_mm_list_lock(smgt);
    mms->page_used = mms->page_cnt;
    list_add_tail(&mms->list, &smgt->scatter_list);
    smgt->scatters_cnt++;
    codec_mm_scatter_map_add_locked(smgt, mms);
    codec_mm_list_unlock(smgt);
    return mms;
error:
    codec_mm_scatter_free_on_nouser_ext(smgt, mms, 1);
    return NULL;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc_new);

/*
 * max pages:
 * want pages now,
 * maybe:
 * max pages == support 4k,need pages;
 * page num = current size need pages;
 */
struct codec_mm_scatter *codec_mm_scatter_alloc(int max_page, int page_num, int istvp)
{
    struct codec_mm_scatter_mgt *smgt;
    struct codec_mm_scatter *mms, *alloced_mms;
    struct list_head *pos, *next;
    int ret;
    u64 startus;

    smgt = codec_mm_get_scatter_mgt(istvp);
    startus = codec_mm_get_current_us();
    alloced_mms = NULL;
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START);
    codec_mm_list_lock(smgt);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START_LOCK);
    if (!list_empty(&smgt->scatter_list)) { /* try find a free scatter. */
        pos = smgt->scatter_list.prev;      /* free on prev. */
        while (pos != &smgt->scatter_list) {
            next = pos->prev;
            mms = list_entry(pos, struct codec_mm_scatter, list);
            if (mms->page_max_cnt >= max_page && atomic_read(&mms->user_cnt) == 0) {
                if (atomic_add_return(1000L, &mms->user_cnt) == 1000L) {
                    mms->page_used = mms->page_cnt;
                    alloced_mms = mms;
                    break;
                } else {
                    atomic_sub(1000L, &mms->user_cnt);
                }
            }
            pos = next;
        }
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_START_LOCK_DONE);
    codec_mm_list_unlock(smgt);
    if (!alloced_mms) {
        /*
         * just alloc mms first,
         * alloc pages later.
         */
        ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_NEW);
        alloced_mms = codec_mm_scatter_alloc_new(smgt, max_page, 0);
        ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_NEW_END);
    }
    if (alloced_mms) {
        ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN);
        ret = codec_mm_scatter_alloc_want_pages_in(smgt, alloced_mms, page_num);
        ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_END);
        if (ret < 0) {
            atomic_sub(1000L, &alloced_mms->user_cnt);
            return NULL;
        }
        codec_mm_update_alloc_time(smgt, startus);
        return alloced_mms;
    }
    return NULL;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc);

static int codec_mm_scatter_alloc_want_pages_in(struct codec_mm_scatter_mgt *smgt, struct codec_mm_scatter *mms,
                                                int want_pages)
{
    int ret = 0;

    if (want_pages > mms->page_max_cnt) {
        return CODEC_MM_S_ERR(100L);
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_LOCK);
    codec_mm_scatter_lock(mms);
    codec_mm_list_lock(smgt);
    mms->page_used = want_pages;
    codec_mm_list_unlock(smgt);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_LOCK_END);
    if (want_pages > mms->page_cnt) {
        ret = codec_mm_page_alloc_all_locked(smgt, &mms->pages_list[mms->page_tail + 1], want_pages - mms->page_cnt,
                                             is_cache_sc(smgt, mms));
        if (ret <= 0) {
            codec_mm_scatter_unlock(mms);
            ERR_LOG("can't alloc want pages %d\n", want_pages);
            return ret;
        }
        mms->page_cnt += ret;
        mms->page_tail += ret;
    }
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK_START);
    codec_mm_list_lock(smgt);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK);
    if (ret > 0 && is_cache_sc(smgt, mms)) {
        smgt->cached_pages += ret;
    }

    codec_mm_list_unlock(smgt);
    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_LIST_LOCK_END);
    codec_mm_scatter_unlock(mms);
    if (smgt->cached_pages < smgt->keep_size_PAGE / 2L) {
        /* try alloc more cache. */
        codec_mm_schedule_delay_work(smgt, 0, 1);
    }
    return 0;
}

int codec_mm_scatter_alloc_want_pages(struct codec_mm_scatter *mms, int want_pages)
{
    struct codec_mm_scatter_mgt *smgt;
    int ret;
    u64 startus;
    if (!mms) {
        return -1;
    }
    smgt = (struct codec_mm_scatter_mgt *)mms->manager;
    startus = codec_mm_get_current_us();
    ret = codec_mm_scatter_alloc_want_pages_in(smgt, mms, want_pages);
    codec_mm_update_alloc_time(smgt, startus);
    return ret;
}
EXPORT_SYMBOL(codec_mm_scatter_alloc_want_pages);

int codec_mm_free_all_free_slots_in(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_slot *slot, *to_free;

    do {
        to_free = NULL;
        codec_mm_list_lock(smgt);
        {
            struct list_head *header, *list;

            header = &smgt->free_list;
            list = header->prev;
            while (list != header) {
                slot = list_entry(list, struct codec_mm_slot, free_list);
                if (slot->alloced_page_num == 0) {
                    list_del_init(&slot->free_list);
                    to_free = slot;
                    break;
                }
                list = list->prev;
            };
        }
        codec_mm_list_unlock(smgt);
        if (!to_free) {
            break;
        }
        codec_mm_slot_free(smgt, to_free);
    } while (1);
    return 0;
}
EXPORT_SYMBOL(codec_mm_free_all_free_slots);

int codec_mm_free_all_free_slots(void)
{
    codec_mm_free_all_free_slots_in(codec_mm_get_scatter_mgt(0));
    codec_mm_free_all_free_slots_in(codec_mm_get_scatter_mgt(1));
    return 0;
}

static int codec_mm_scatter_info_dump_in(struct codec_mm_scatter_mgt *smgt, void *buf, int size)
{
    char *pbuf = buf;
    char sbuf[512];
    int tsize = 0;
    int s;
    int n;

    if (!pbuf) {
        pbuf = sbuf;
    }

#define BUFPRINT(args...)                                                                                              \
    do {                                                                                                               \
        s = sprintf(pbuf, args);                                                                                       \
        tsize += s;                                                                                                    \
        pbuf += s;                                                                                                     \
    } while (0)

    BUFPRINT("codec %sscattered memory info:\n", smgt->tvp_mode ? "TVP " : "");
    BUFPRINT("\ttotal size:%dM, %d Bytes,pages:%d\n", (smgt->total_page_num << PAGE_SHIFT) / SZ_1M,
             smgt->total_page_num << PAGE_SHIFT, smgt->total_page_num);
    n = smgt->alloced_page_num;
    BUFPRINT("\talloced size:%dM, %d Bypes,pages:%d\n", (n << PAGE_SHIFT) / SZ_1M, n << PAGE_SHIFT, n);
    BUFPRINT("\tmax alloced:%d M | %d pages\n", (smgt->max_alloced << PAGE_SHIFT) / SZ_1M, smgt->max_alloced);
    BUFPRINT("\tscatter cached:%d M |%d pages\n", (smgt->cached_pages << PAGE_SHIFT) / SZ_1M, smgt->cached_pages);

    BUFPRINT("\talloc from sys size:%d\n", (smgt->alloc_from_sys_sc_cnt + smgt->one_page_cnt) << PAGE_SHIFT);

    BUFPRINT("\talloc from sys sc cnt:%d\n", smgt->alloc_from_sys_sc_cnt);
    BUFPRINT("\talloc from sys pages cnt:%d pages\n", smgt->alloc_from_sys_page_cnt);
    BUFPRINT("\talloc from sys max pages cnt:%d pages\n", smgt->alloc_from_sys_max_page_cnt);
    BUFPRINT("\tscatter_task_run:%d\n", smgt->scatter_task_run_num);
    BUFPRINT("\tone_page_cnt:%d\n", smgt->one_page_cnt);
    BUFPRINT("\tcatters cnt:%d\n", smgt->scatters_cnt);
    BUFPRINT("\tslot cnt:%d\n", smgt->slot_cnt);
    BUFPRINT("\tcma alloc block size:%d\n", smgt->try_alloc_in_cma_page_cnt);
    BUFPRINT("\tsys alloc block size:%d\n", smgt->try_alloc_in_sys_page_cnt);
    BUFPRINT("\tdelay_free_on:%d\n", smgt->delay_free_on);
    BUFPRINT("\tdelay_free_on time:%lld jiffies\n", smgt->delay_free_timeout_jiffies64);
    BUFPRINT("\tcurrent time:%lld\n", get_jiffies_64());
    BUFPRINT("\talloc time max us:%d\n", smgt->alloc_max_us);
    BUFPRINT("\talloc cnt:%d step:%d:%d:%d:%d:%d:%d:%d\n", smgt->alloc_cnt, smgt->alloc_10us_less_cnt,
             smgt->alloc_10_50us_cnt, smgt->alloc_50_100us_cnt, smgt->alloc_100_1000us_cnt, smgt->alloc_1_10ms_cnt,
             smgt->alloc_10_100ms_cnt, smgt->alloc_100ms_up_cnt);
    BUFPRINT("\tfree time max us:%d\n", smgt->free_max_us);
    BUFPRINT("\tfree cnt:%d step:%d:%d:%d:%d:%d:%d:%d\n", smgt->free_cnt, smgt->free_10us_less_cnt,
             smgt->free_10_50us_cnt, smgt->free_50_100us_cnt, smgt->free_100_1000us_cnt, smgt->free_1_10ms_cnt,
             smgt->free_10_100ms_cnt, smgt->free_100ms_up_cnt);
    {
        int average_timeus;
        u64 divider = smgt->alloc_total_us;

        do_div(divider, smgt->alloc_cnt);
        average_timeus = (smgt->alloc_cnt == 0 ? 0 : (int)divider);
        BUFPRINT("\talloc time average us:%d\n", average_timeus);
        divider = smgt->free_total_us;

        do_div(divider, smgt->free_cnt);
        average_timeus = (smgt->free_cnt == 0 ? 0 : (int)divider);
        BUFPRINT("\tfree time average us:%d\n", average_timeus);
    }

#undef BUFPRINT
    if (!buf) {
        INFO_LOG("%s", sbuf);
    }
    return tsize;
}
EXPORT_SYMBOL(codec_mm_scatter_info_dump);

int codec_mm_scatter_info_dump(void *buf, int size)
{
    char *pbuf = buf;
    int esize = size;
    int ret;

    ret = codec_mm_scatter_info_dump_in(codec_mm_get_scatter_mgt(0), pbuf, esize);
    if (buf != NULL && ret > 0) {
        pbuf += ret;
        esize -= ret;
    }
    ret += codec_mm_scatter_info_dump_in(codec_mm_get_scatter_mgt(1), pbuf, esize);
    return ret;
}

int codec_mm_dump_slot(struct codec_mm_slot *slot, void *buf, int size)
{
    char *pbuf = buf;
    char sbuf[512];
    int tsize = 0;
    int s;
    int i;
    int sum;
    char bits_incharNhalf[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 2, 3, 4, 1, 2};

    if (!pbuf) {
        pbuf = sbuf;
    }

#define BUFPRINT(args...)                                                                                              \
    do {                                                                                                               \
        s = sprintf(pbuf, args);                                                                                       \
        tsize += s;                                                                                                    \
        pbuf += s;                                                                                                     \
    } while (0)

    BUFPRINT("slot info:%p\n", slot);
    BUFPRINT("\tfrom:%s\n", (slot->from_type == SLOT_FROM_CODEC_MM ? "codec_mm" : "sys"));
    BUFPRINT("\tbase addr range:%p<-->%p\n", (void *)slot->phy_addr,
             (void *)(slot->phy_addr + (slot->page_num << PAGE_SHIFT) - 1));
    BUFPRINT("\tpage_num:%d\n", slot->page_num);
    BUFPRINT("\talloced:%d,free:%d\n", slot->alloced_page_num, slot->page_num - slot->alloced_page_num);
    BUFPRINT("\tnext bit:%d\n", slot->next_bit);
    BUFPRINT("\tsid:%x\n", slot->sid);
    BUFPRINT("\troot:%d\n", slot->isroot);
    sum = 0;
    for (i = 0; i < slot->pagemap_size; i++) {
        int c = ((unsigned char *)slot->pagemap)[i];

        sum += bits_incharNhalf[c & 0xf] + bits_incharNhalf[c >> 4L];
    }
    BUFPRINT("\tbitmap.setbits.sum:%d\n", sum);
    BUFPRINT("\tbitmap");
    i = 0;
    while (i < slot->pagemap_size && i < 16L) {
        BUFPRINT(":%02x", ((char *)slot->pagemap)[i++]);
    }
    BUFPRINT("\n");
#undef BUFPRINT
    if (!buf) {
        INFO_LOG("%s", sbuf);
    }
    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_slot);

int codec_mm_dump_all_slots_in(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_slot *slot, *fslot;
    int total_pages = 0;
    int alloced_pages = 0;
    int slot_cnt = 0;
    int i;

    codec_mm_list_lock(smgt);
    INFO_LOG("start dump all slots!\n");
    for (i = 0; i < MAX_SID; i++) {
        fslot = smgt->slot_list_map[i];
        if (fslot) {
            codec_mm_dump_slot(fslot, NULL, 0);
            slot_cnt++;
            total_pages += fslot->page_num;
            alloced_pages += fslot->alloced_page_num;
            if (!list_empty(&fslot->sid_list)) {
                slot = list_entry(fslot->sid_list.next, struct codec_mm_slot, sid_list);
                while (slot != fslot) {
                    codec_mm_dump_slot(slot, NULL, 0);
                    slot_cnt++;
                    total_pages += slot->page_num;
                    alloced_pages += slot->alloced_page_num;
                    slot = list_entry(slot->sid_list.next, struct codec_mm_slot, sid_list);
                }
            }
        }
    }
    codec_mm_list_unlock(smgt);
    INFO_LOG("end dump, slot cnt:%d total pages:%d, free:%d\n", slot_cnt, total_pages, total_pages - alloced_pages);
    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_slots);

int codec_mm_dump_all_slots(void)
{
    codec_mm_dump_all_slots_in(codec_mm_get_scatter_mgt(0));
    codec_mm_dump_all_slots_in(codec_mm_get_scatter_mgt(1));
    return 0;
}

int codec_mm_dump_all_hash_table_in(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_slot *slot, *fslot;
    int i;
    int total_pages = 0;
    int alloced_pages = 0;

    INFO_LOG("start dump sid hash table!\n");
    codec_mm_list_lock(smgt);
    for (i = 0; i < MAX_SID; i++) {
        int cnt = 0;
        int pages = 0;
        int alloced = 0;

        fslot = smgt->slot_list_map[i];
        if (fslot) {
            cnt++;
            pages += fslot->page_num;
            alloced += fslot->alloced_page_num;
            if (!list_empty(&fslot->sid_list)) {
                slot = list_entry(fslot->sid_list.next, struct codec_mm_slot, sid_list);
                while (slot != fslot) {
                    cnt++;
                    pages += slot->page_num;
                    alloced += slot->alloced_page_num;
                    slot = list_entry(slot->sid_list.next, struct codec_mm_slot, sid_list);
                }
            }
        }
        if (cnt > 0) {
            total_pages += pages;
            alloced_pages += alloced;
            INFO_LOG("\tSID(%d):\tslots:%d,\tpages:%d:\tfree pages:%d\n", i, cnt, pages, pages - alloced);
        }
    }
    codec_mm_list_unlock(smgt);
    INFO_LOG("end dump sid hash table, total pages:%d, free:%d\n", total_pages, total_pages - alloced_pages);
    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_hash_table);

int codec_mm_dump_all_hash_table(void)
{
    codec_mm_dump_all_hash_table_in(codec_mm_get_scatter_mgt(0));
    codec_mm_dump_all_hash_table_in(codec_mm_get_scatter_mgt(1));
    return 0;
}

static int codec_mm_dump_free_slots_in(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_slot *slot;
    int total_pages = 0;
    int alloced_pages = 0;

    INFO_LOG("dump all free slots:\n");
    codec_mm_list_lock(smgt);
    if (!list_empty(&smgt->free_list)) {
        struct list_head *header, *list;

        header = &smgt->free_list;
        list = header->prev;
        while (list != header) {
            slot = list_entry(list, struct codec_mm_slot, free_list);
            codec_mm_dump_slot(slot, NULL, 0);
            total_pages += slot->page_num;
            alloced_pages += slot->alloced_page_num;
            list = list->prev;
        };
    }
    codec_mm_list_unlock(smgt);
    INFO_LOG("end all free slots: total pages:%d, freed:%d\n", total_pages, total_pages - alloced_pages);
    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_free_slots);

int codec_mm_dump_free_slots(void)
{
    codec_mm_dump_free_slots_in(codec_mm_get_scatter_mgt(0));
    codec_mm_dump_free_slots_in(codec_mm_get_scatter_mgt(1));
    return 0;
}

int codec_mm_dump_scatter(struct codec_mm_scatter *mms, void *buf, int size)
{
    char *pbuf = buf;
    char sbuf[512];
    int tsize = 0;
    int s;
    int i;

    if (!pbuf) {
        pbuf = sbuf;
    }

#define BUFPRINT(args...)                                                                                              \
    do {                                                                                                               \
        s = sprintf(pbuf, args);                                                                                       \
        tsize += s;                                                                                                    \
        pbuf += s;                                                                                                     \
    } while (0)

    BUFPRINT("scatter info:%p\n", mms);
    BUFPRINT("\tsize:%d\n", (int)(mms->page_cnt * PAGE_SIZE));
    BUFPRINT("\tmax:%d\n", mms->page_max_cnt);
    BUFPRINT("\tpage_cnt:%d\n", mms->page_cnt);
    BUFPRINT("\tpage_used:%d\n", mms->page_used);
    BUFPRINT("\tpage_tail:%d\n", mms->page_tail);
    BUFPRINT("\tuser_cnt:%d\n", atomic_read(&mms->user_cnt));
    BUFPRINT("\ttofree_jiffies:%ld\n", mms->tofree_jiffies);

    i = 0;
    while (i < mms->page_cnt && i < 16L) {
        BUFPRINT(":%x", (u32)mms->pages_list[i++]);
    }
    BUFPRINT("\n");
#undef BUFPRINT
    if (!buf) {
        INFO_LOG("%s", sbuf);
    }

    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_scatter);

static int codec_mm_dump_all_scatters_in(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_scatter *mms;
    struct list_head *pos, *tmp;

    INFO_LOG("start dump all scatters!\n");
    codec_mm_list_lock(smgt);
    do {
        if (list_empty(&smgt->scatter_list)) {
            break;
        }
        list_for_each_safe(pos, tmp, &smgt->scatter_list)
        {
            mms = list_entry(pos, struct codec_mm_scatter, list);
            codec_mm_dump_scatter(mms, 0, 0);
        }
    } while (0);
    INFO_LOG("start dump free scatters!\n");
    if (smgt->cache_scs[0]) {
        codec_mm_dump_scatter(smgt->cache_scs[0], 0, 0);
    }
    if (smgt->cache_scs[1]) {
        codec_mm_dump_scatter(smgt->cache_scs[1], 0, 0);
    }
    codec_mm_list_unlock(smgt);
    INFO_LOG("finished dump all scatters!\n");
    return 0;
}
EXPORT_SYMBOL(codec_mm_dump_all_scatters);

int codec_mm_dump_all_scatters(void)
{
    codec_mm_dump_all_scatters_in(codec_mm_get_scatter_mgt(0));
    codec_mm_dump_all_scatters_in(codec_mm_get_scatter_mgt(1));
    return 0;
}

int codec_mm_scatter_update_config(struct codec_mm_scatter_mgt *smgt)
{
    smgt->keep_size_PAGE = g_scatter.keep_size_PAGE;
    smgt->reserved_block_mm_M = g_scatter.reserved_block_mm_M;
    smgt->try_alloc_in_cma_page_cnt = g_scatter.try_alloc_in_cma_page_cnt;
    smgt->try_alloc_in_sys_page_cnt_max = g_scatter.try_alloc_in_sys_page_cnt_max;
    smgt->try_alloc_in_sys_page_cnt_min = g_scatter.try_alloc_in_sys_page_cnt_min;
    smgt->enable_slot_from_sys = g_scatter.enable_slot_from_sys;
    smgt->support_from_slot_sys = g_scatter.support_from_slot_sys;
    smgt->no_cache_size_M = g_scatter.no_cache_size_M;
    smgt->no_alloc_from_sys = g_scatter.no_alloc_from_sys;
    return 0;
}
int codec_mm_scatter_size(int is_tvp)
{
    struct codec_mm_scatter_mgt *smgt;
    smgt = codec_mm_get_scatter_mgt(is_tvp ? 1 : 0);

    return smgt->total_page_num;
}
EXPORT_SYMBOL(codec_mm_scatter_size);

int codec_mm_scatter_mgt_delay_free_swith(int on, int delay_ms, int wait_size_M, int is_tvp)
{
    struct codec_mm_scatter_mgt *smgt;

    smgt = codec_mm_get_scatter_mgt(is_tvp);
    codec_mm_list_lock(smgt);
    if (on) {
        smgt->delay_free_on++;
        smgt->delay_free_timeout_jiffies64 = get_jiffies_64() + delay_ms * HZ / 1000L;
    } else {
        smgt->delay_free_on--;
        if (smgt->delay_free_on <= 0) {
            smgt->delay_free_on = 0;
            smgt->delay_free_timeout_jiffies64 = get_jiffies_64() + delay_ms * HZ / 1000L;
        }
    }
    codec_mm_list_unlock(smgt);
    if (on && wait_size_M > 0 /* && !is_tvp */) {
        smgt->force_cache_on = 1;
        smgt->force_cache_page_cnt = wait_size_M >> PAGE_SHIFT;
        smgt->delay_free_timeout_jiffies64 = get_jiffies_64() + 10000L * HZ / 1000L;
        codec_mm_schedule_delay_work(smgt, 0, 1); /* start cache */
    } else if (on) {
        codec_mm_schedule_delay_work(smgt, 0, 1);
    } else {
        codec_mm_schedule_delay_work(smgt, delay_ms, 0);
    }
    return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_mgt_delay_free_swith);

static void codec_mm_scatter_cache_manage(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_scatter *mms;
    int alloced = 0;
    int total_free_page = smgt->total_page_num - smgt->alloced_page_num + smgt->cached_pages;

    if (smgt->delay_free_on > 0 && smgt->keep_size_PAGE > 0) {
        /* if alloc too much ,don't cache any more. */
        if (smgt->no_cache_size_M > 0 && (smgt->cached_pages <= smgt->keep_size_PAGE) &&
            (smgt->total_page_num >= (smgt->no_cache_size_M * (SZ_1M >> PAGE_SHIFT)))) {
            /* have enough pages for most movies. */
            /* don't cache more. */
            if (smgt->force_cache_on) {
                smgt->force_cache_on = 0;
                smgt->delay_free_timeout_jiffies64 = get_jiffies_64() + 2000L * HZ / 1000L;
            }
        } else if ((smgt->cached_pages < smgt->keep_size_PAGE) ||
                   (smgt->force_cache_on &&                                 /* on star cache */
                    (smgt->total_page_num < smgt->force_cache_page_cnt))) { /* first 500ms ,alloc double. */
            mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
            if (mms) {
                int need;
                int once_alloc = 1000L; /* once 4M */

                if (smgt->force_cache_on) {
                    once_alloc = 4000L;
                    if ((smgt->total_page_num - smgt->alloced_page_num) > once_alloc) {
                        once_alloc = smgt->total_page_num - smgt->alloced_page_num;
                    }
                }
                need = mms->page_cnt + once_alloc;
                if ((need - mms->page_cnt) > once_alloc) {
                    need = mms->page_cnt + once_alloc;
                }
                if (need > smgt->force_cache_page_cnt) {
                    need = smgt->force_cache_page_cnt;
                }
                if (need > mms->page_max_cnt) {
                    need = mms->page_max_cnt - 4L;
                }
                if (need > mms->page_cnt) {
                    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_2);
                    alloced = !codec_mm_scatter_alloc_want_pages_in(smgt, mms, need);
                    ATRACE_COUNTER("mmu alloc", MMU_ALLOC_SCATTER_ALLOC_WANT_PAGE_IN_2_END);
                } else {
                    alloced = 0;
                }
            } else {
                alloced = 0;
            }
            /* wake up wait. */
            if (alloced && smgt->force_cache_on && (smgt->cached_pages >= smgt->force_cache_page_cnt)) {
                smgt->force_cache_on = 0;
                smgt->delay_free_timeout_jiffies64 = get_jiffies_64() + 2000L * HZ / 1000L;
            }
        } else if ((smgt->cached_pages > (smgt->keep_size_PAGE + 1000L)) &&
                   time_after64(get_jiffies_64(), smgt->delay_free_timeout_jiffies64)) {
            /* wait time out can free. */
            mms = codec_mm_get_next_cache_scatter(smgt, NULL, 0);
            if (mms) { /* only free some 1M cache */
                codec_mm_scatter_less_pages(mms, 256L);
            }
            codec_mm_free_all_free_slots_in(smgt);
            /* free some slots. */
        }
    } else if (smgt->delay_free_on <= 0 && time_after64(get_jiffies_64(), smgt->delay_free_timeout_jiffies64)) {
        /* free all free pages, no delay needed. */
        codec_mm_free_all_free_slots_in(smgt);
    }
    if (smgt->keep_size_PAGE > 0 && smgt->delay_free_on) {
        if (((smgt->force_cache_on || (total_free_page < smgt->keep_size_PAGE)) /* && */
             /* !smgt->tvp_mode */) &&
            alloced) { /* if failed may deadlock... */
            /* ignore keep on tvp mode. */
            if (smgt->force_cache_on && !smgt->tvp_mode) {
                codec_mm_schedule_delay_work(smgt, 0, 1);
            } else {
                codec_mm_schedule_delay_work(smgt, 10L, 0);
            }
        } else {
            codec_mm_schedule_delay_work(smgt, 100L, 0);
        }
    } else if (!smgt->delay_free_on && smgt->total_page_num > 0) {
        codec_mm_schedule_delay_work(smgt, 100L, 0);
    }
}

static int codec_mm_scatter_scatter_arrange(struct codec_mm_scatter_mgt *smgt)
{
    struct codec_mm_scatter *mms;
    struct codec_mm_scatter *first_free_mms = NULL;
    struct list_head *pos, *tmp;
    int n = 0;

    if (smgt->delay_free_on > 0 && !smgt->cache_scs[0]) {
        /* cache1 */
        mms = codec_mm_scatter_alloc_new(smgt, 16384L, 0);
        if (mms) {
            codec_mm_list_lock(smgt);
            list_del_init(&mms->list);
            smgt->cache_scs[0] = mms;
            codec_mm_list_unlock(smgt);
        }
        /* cache2 */
        mms = codec_mm_scatter_alloc_new(smgt, 16384L, 0);
        if (mms) {
            codec_mm_list_lock(smgt);
            list_del_init(&mms->list);
            smgt->cache_scs[1] = mms;
            codec_mm_list_unlock(smgt);
        }
    }
    if (smgt->delay_free_on <= 0 && smgt->cache_scs[0] &&
        time_after64(get_jiffies_64(), smgt->delay_free_timeout_jiffies64)) {
        struct codec_mm_scatter *mms0, *mms1;
        codec_mm_list_lock(smgt);
        mms0 = smgt->cache_scs[0];
        mms1 = smgt->cache_scs[1];
        smgt->cache_scs[0] = NULL;
        smgt->cache_scs[1] = NULL;
        smgt->cached_pages = 0;
        codec_mm_list_unlock(smgt);
        if (mms0) {
            codec_mm_scatter_dec_owner_user(mms0, 0);
            codec_mm_scatter_free_on_nouser(smgt, mms0);
        }
        if (mms1) {
            codec_mm_scatter_dec_owner_user(mms1, 0);
            codec_mm_scatter_free_on_nouser(smgt, mms1);
        }
    }

    codec_mm_list_lock(smgt);
    if (list_empty(&smgt->scatter_list)) {
        codec_mm_list_unlock(smgt);
        return 0;
    }
    list_for_each_safe(pos, tmp, &smgt->scatter_list)
    {
        mms = list_entry(pos, struct codec_mm_scatter, list);
        if (atomic_read(&mms->user_cnt) == 0 && time_after(jiffies, mms->tofree_jiffies)) {
            if (!first_free_mms || mms->tofree_jiffies < first_free_mms->tofree_jiffies) {
                first_free_mms = mms;
            }
        } else {
            n++;
        }
    }
    if (first_free_mms) {
        list_move_tail(&mms->list, &smgt->scatter_list);
    }
    codec_mm_list_unlock(smgt);

    return 0;
}

/* not check jiffies & cache */
static int codec_mm_scatter_scatter_clear(struct codec_mm_scatter_mgt *smgt, int force)
{
    struct codec_mm_scatter *mms, *to_free_mms, *less_page_mms;
    struct list_head *pos, *tmp;
    int to_free_mms_cnt = 0;

    codec_mm_list_lock(smgt);
    to_free_mms = NULL;
    less_page_mms = NULL;
    list_for_each_safe(pos, tmp, &smgt->scatter_list)
    {
        mms = list_entry(pos, struct codec_mm_scatter, list);
        if (atomic_read(&mms->user_cnt) == 0) {
            if (!to_free_mms || (mms->tofree_jiffies < to_free_mms->tofree_jiffies)) {
                to_free_mms = mms;
            }
            to_free_mms_cnt++;
        }
        if (!less_page_mms && mms->page_used < mms->page_cnt) {
            less_page_mms = mms;
            break;
        }
    }

    if ((to_free_mms != NULL) &&
        (((to_free_mms_cnt > 1 || !smgt->delay_free_on) && time_after(jiffies, to_free_mms->tofree_jiffies)) ||
         force)) { /* force== no checktimeer. */
        /* set to nagative for free now. */
        int cnt = atomic_sub_return(100000L, &to_free_mms->user_cnt);
        if (cnt != -100000L) {
            atomic_add(100000L, &to_free_mms->user_cnt);
            to_free_mms = NULL;
        } else {
            list_del_init(&to_free_mms->list);
        }
    } else {
        to_free_mms = NULL;
    }
    codec_mm_list_unlock(smgt);
    if (to_free_mms != NULL) {
        codec_mm_scatter_free_on_nouser(smgt, to_free_mms);
    }
    if (less_page_mms && (less_page_mms != to_free_mms)) {
        codec_mm_scatter_free_unused_pages(less_page_mms);
    }
    return (to_free_mms != NULL) || (less_page_mms != NULL);
}

/*
 * clear all ignore any cache.
 *
 * return the total num alloced.
 * 0 is all freeed.
 * N is have some pages not alloced.
 * flags: 0: cache only,
 *       1: all no user;
 *       2: all, ignore the time check.
 */
static int codec_mm_scatter_free_all_ignorecache_in(struct codec_mm_scatter_mgt *smgt, int flags)
{
    int need_retry = 1;
    int retry_num = 0;

    mutex_lock(&smgt->monitor_lock);
    pr_info("force free all scatter ignorecache!\n");
    /* free cache: always */
    do {
        struct codec_mm_scatter *mms;
        /* clear cache first. */
        /* disabled free on first. */
        smgt->delay_free_on = 0;
        codec_mm_list_lock(smgt);
        mms = smgt->cache_scs[0];
        smgt->cache_scs[0] = smgt->cache_scs[1];
        smgt->cache_scs[1] = NULL;
        smgt->cached_pages = 0;
        codec_mm_list_unlock(smgt);
        if (mms) {
            pr_info("cache_sc page_max %d, page_used %d\n", mms->page_max_cnt, mms->page_used);
            codec_mm_scatter_dec_owner_user(mms, 0);
            codec_mm_scatter_free_on_nouser(smgt, mms);
        }
        /* alloced again on timer? */
        /* check again. */
    } while (smgt->cache_scs[0] != NULL);
    /* free cache nouser
     * if flags==2  force
     */
    if (flags >= 1) {
        do {
            need_retry = codec_mm_scatter_scatter_clear(smgt, flags == 2L);
        } while ((smgt->scatters_cnt > 0) && (retry_num++ < 1000L));
    }

    if (need_retry || smgt->scatters_cnt > 0) {
        pr_info("can't free all scatter, because some have used!!\n");
    }
    codec_mm_free_all_free_slots_in(smgt);
    if (smgt->total_page_num > 0) {
        /* have some not free,dump tables for debug */
        pr_info("Some slots have not free!!\n\n");
    }
    mutex_unlock(&smgt->monitor_lock);
    return smgt->total_page_num;
}
EXPORT_SYMBOL(codec_mm_scatter_free_all_ignorecache);

int codec_mm_scatter_free_all_ignorecache(int flags)
{
    if (flags & 1) {
        codec_mm_scatter_free_all_ignorecache_in(codec_mm_get_scatter_mgt(0), 2L);
    }
    if (flags & 2L) { /* free cache and unused pages */
        codec_mm_scatter_free_all_ignorecache_in(codec_mm_get_scatter_mgt(1), 1);
    }
    return 0;
}

static void codec_mm_scatter_monitor(struct work_struct *work)
{
    struct codec_mm_scatter_mgt *smgt = container_of(work, struct codec_mm_scatter_mgt, dealy_work.work);
    int needretry = 0;

    codec_mm_scatter_update_config(smgt);
    mutex_lock(&smgt->monitor_lock);
    smgt->scatter_task_run_num++;

    codec_mm_scatter_scatter_arrange(smgt);
    needretry = codec_mm_scatter_scatter_clear(smgt, 0);
    if (needretry) {
        codec_mm_schedule_delay_work(smgt, 10L, 0);
    } else if (smgt->scatters_cnt > 0) {
        codec_mm_schedule_delay_work(smgt, 100L, 0);
    }
    codec_mm_scatter_cache_manage(smgt);
    mutex_unlock(&smgt->monitor_lock);
}

static int codec_mm_scatter_mgt_alloc_in(struct codec_mm_scatter_mgt **psmgt)
{
    struct codec_mm_scatter_mgt *smgt;

    smgt = kmalloc(sizeof(struct codec_mm_scatter_mgt), GFP_KERNEL);
    if (!smgt) {
        ERR_LOG("ERR:codec mm mpt init ERROR\n");
        return -1;
    }
    memset(smgt, 0, sizeof(struct codec_mm_scatter_mgt));
    spin_lock_init(&smgt->list_lock);
    smgt->tag = SMGT_IDENTIFY_TAG;
    smgt->alloced_page_num = 0;
    smgt->try_alloc_in_cma_page_cnt = (16L * 1024L * 1024L) / PAGE_SIZE;
    smgt->try_alloc_in_sys_page_cnt_max = MAX_SYS_BLOCK_PAGE;
    smgt->try_alloc_in_sys_page_cnt = MAX_SYS_BLOCK_PAGE;
    smgt->try_alloc_in_sys_page_cnt_min = MIN_SYS_BLOCK_PAGE;
    smgt->reserved_block_mm_M = 300L;
    smgt->keep_size_PAGE = 20L * SZ_1M >> PAGE_SHIFT;
    smgt->alloc_from_cma_first = 1;
    smgt->enable_slot_from_sys = 0;
    smgt->support_from_slot_sys = smgt->enable_slot_from_sys;
    smgt->mem_flags = CODEC_MM_FLAGS_CMA_FIRST | CODEC_MM_FLAGS_FOR_VDECODER | CODEC_MM_FLAGS_FOR_SCATTER;
    if ((totalram_pages() << PAGE_SHIFT) < 800L * SZ_1M) {
        /* less memory boards don't cache more, */
        /* after alloced many pages. */
        smgt->no_cache_size_M = 100L;
    } else {
        smgt->no_cache_size_M = 0;
    }
    init_completion(&smgt->complete);
    INIT_LIST_HEAD(&smgt->free_list);
    INIT_LIST_HEAD(&smgt->scatter_list);
    mutex_init(&smgt->monitor_lock);

    INIT_DELAYED_WORK(&smgt->dealy_work, codec_mm_scatter_monitor);
    *psmgt = smgt;
    return 0;
}

static struct mconfig codec_mm_sc_configs[] = {
    MC_PU32("keep_size_PAGE", &g_scatter.keep_size_PAGE),
    MC_PU32("reserved_block_mm_M", &g_scatter.reserved_block_mm_M),
    MC_PU32("try_alloc_in_cma_page_cnt", &g_scatter.try_alloc_in_cma_page_cnt),
    MC_PU32("try_alloc_in_sys_page_cnt_max", &g_scatter.try_alloc_in_sys_page_cnt_max),
    MC_PU32("try_alloc_in_sys_page_cnt_min", &g_scatter.try_alloc_in_sys_page_cnt_min),
    MC_PU32("enable_slot_from_sys", &g_scatter.enable_slot_from_sys),
    MC_PU32("no_cache_size_M", &g_scatter.no_cache_size_M),
    MC_PU32("no_alloc_from_sys", &g_scatter.no_alloc_from_sys),
};

static struct mconfig_node codec_mm_sc;

int codec_mm_scatter_mgt_init(void)
{
    struct codec_mm_scatter_mgt *smgt;

    codec_mm_scatter_mgt_alloc_in(&scatter_mgt);
    codec_mm_scatter_mgt_alloc_in(&scatter_tvp_mgt);
    scatter_tvp_mgt->tvp_mode = 1;
    scatter_tvp_mgt->mem_flags |= CODEC_MM_FLAGS_TVP;
    smgt = scatter_mgt;
    g_scatter.keep_size_PAGE = smgt->keep_size_PAGE;
    g_scatter.reserved_block_mm_M = smgt->reserved_block_mm_M;
    g_scatter.try_alloc_in_cma_page_cnt = smgt->try_alloc_in_cma_page_cnt;
    g_scatter.try_alloc_in_sys_page_cnt_max = smgt->try_alloc_in_sys_page_cnt_max;
    g_scatter.try_alloc_in_sys_page_cnt_min = smgt->try_alloc_in_sys_page_cnt_min;
    g_scatter.enable_slot_from_sys = smgt->enable_slot_from_sys;
    g_scatter.support_from_slot_sys = smgt->support_from_slot_sys;
    g_scatter.no_cache_size_M = smgt->no_cache_size_M;
    g_scatter.no_alloc_from_sys = 0;
    INIT_REG_NODE_CONFIGS("media.codec_mm", &codec_mm_sc, "scatter", codec_mm_sc_configs, CONFIG_FOR_RW);
    return 0;
}

int codec_mm_scatter_mgt_test(void)
{
    return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_mgt_test);

/*
 * mode:0,dump, 1,alloc 2,more,3,free some,4,free all
 * 0:dump ALL
 * 1: alloc id,num
 * 2: alloc more  id,num
 * 3: free tail  id,start
 * 4: free all  id
 * 5:dump id
 * 6:dump all free slots
 */
int codec_mm_scatter_test(int mode, int p1, int p2)
{
    static int init;
    static struct codec_mm_scatter *sc[64];

    if (!init) {
        init++;
        memset(sc, 0, sizeof(sc));
    }
    switch (mode) {
        case 1: /* alloc */
            INFO_LOG(" alloc sc[%d] num %d:\n", p1, p2);
            if (p1 > 0 && p1 < 64L) {
                if (sc[p1]) {
                    codec_mm_scatter_free_on_nouser((struct codec_mm_scatter_mgt *)sc[p1]->manager, sc[p1]);
                }
                sc[p1] = codec_mm_scatter_alloc(p2 * 2L, p2, 0);
            }
            break;
        case 2L: /* alloc more */
            INFO_LOG(" alloc more sc[%d] num %d\n", p1, p2);
            if (p1 > 0 && p1 < 64L && sc[p1]) {
                codec_mm_scatter_alloc_want_pages(sc[p1], p2);
            }
            break;
        case 3L: /* alloc tails */
            INFO_LOG(" free some sc[%d] start free id %d\n", p1, p2);
            if (p1 > 0 && p1 < 64L && sc[p1]) {
                codec_mm_scatter_free_tail_pages(sc[p1], p2);
            }
            break;
        case 4L:
            INFO_LOG(" free sc[%d] all\n", p1);
            if (p1 > 0 && p1 < 64L && sc[p1]) {
                codec_mm_scatter_free_on_nouser((struct codec_mm_scatter_mgt *)sc[p1]->manager, sc[p1]);
                sc[p1] = NULL;
            }
            break;
        case 5L:
            INFO_LOG(" sc %d info:\n", p1);
            if (p1 > 0 && p1 < 64L && sc[p1]) {
                codec_mm_dump_scatter(sc[p1], NULL, 0);
            }
            break;
        case 0:
        default: {
            int i;

            INFO_LOG(" dump all test sc info:\n");
            for (i = 0; i < 64L; i++) {
                if (sc[i] != NULL) {
                    INFO_LOG(" alloc sc[%d] has data\n", i);
                    codec_mm_dump_scatter(sc[i], NULL, 0);
                }
            }
        }
            break;
    }
    return 0;
}
EXPORT_SYMBOL(codec_mm_scatter_test);
